/*
 * Project CelestialBeing: base_meta.hpp
 * Created: 2023/12/08
 * This program uses MulanPSL2. See LICENSE for more.
 */

#ifndef CELESTIALBEING_BASE_META_HPP
#define CELESTIALBEING_BASE_META_HPP

#include <tuple>
#include "cb/common/celestial_being_base.hpp"

CB_NAMESPACE_BEGIN

// Meta-programming utilities.

namespace details {
    // Use 'zz' for internal function / class prefix (they are in the later part of the alphabet).
    // Besides, all internal code should be in the detail namespace.
    template<typename T, template<typename...> typename>
    struct zzTIsInstanceOfTemplate : std::false_type {};
    template<template<typename...> class Tmpl, typename...ArgsT>
    struct zzTIsInstanceOfTemplate<Tmpl<ArgsT...>, Tmpl> : public std::true_type {};
}

// Check if a class is a template instance.
// This only works on templates like template<typename T1, ..., typename Tn> class Tmpl;
// See https://en.cppreference.com/w/cpp/language/template_parameters for more information.
template<typename T, template<typename...> class Tmpl>
struct  TIsInstanceOfTemplate : details::zzTIsInstanceOfTemplate<std::remove_cvref_t<T>, Tmpl> {};

// We cant use auto... in template template parameters. So reach out for special cases.
// Check if a class is an instance of std::array.
template<typename T> struct TIsInstanceOfSTLArrayTemplate : std::false_type {};
template<typename Te, size_t N> struct TIsInstanceOfSTLArrayTemplate<std::array<Te, N>> : std::true_type {};

// Concept for pure old data (POD).
// See https://en.cppreference.com/w/cpp/language/pod for more information.
template<typename T> concept CPureOldData = std::is_pod_v<T>;

namespace details {
    template<typename T, size_t Num, typename...Args>
    struct zzTTTupleType {
        using value = zzTTTupleType<T, Num - 1, T, Args...>::value;
    };
    template<typename T, typename...Args>
    struct zzTTTupleType<T, 0, Args...> {
        using value = std::tuple<Args...>;
    };
}

// Use 'T' for template prefix.

// Get the tuple type "tuple<T, T, T, ...>" from a type T and a number Num.
template<typename T, size_t Num>
struct TTupleType {
    using value = typename details::zzTTTupleType<T, Num>::value;
};

// Check if a class is defined or just forward declared.
// This is useful when we want to check if a class is defined.
// For example, we can use this to check if a class is defined in a header file.
template<typename T>
struct TIsComplete {
private:
    // Note: sizeof(T) is only valid when T is a complete type. Make use of SFINAE here.
    template<class U>
    static constexpr bool Check (decltype(sizeof(U))) {
        return true;
    }
    template<class U>
    static constexpr bool Check (...) {
        return false;
    }
public:
    static_assert(std::is_class_v<T> && "TIsComplete can only be used on classes. ");
    static constexpr bool value = Check<T>(0);
};

template<typename ...>
struct TDelayedFalseType : std::false_type {};

template<typename T, typename IDType> concept CUint64ID = std::is_same_v<uint64_t, T> || std::is_same_v<IDType, T>;

namespace details {
    template<const auto &Arr, typename = std::make_index_sequence<std::extent_v<std::remove_cvref_t<decltype(Arr)>>>>
    struct TCArrayToCPPArray ;
    template<const auto &Arr, size_t...Indices>
    struct TCArrayToCPPArray<Arr, std::integer_sequence<size_t, Indices...>> {
        using ArrType = std::remove_cvref_t<decltype(Arr)>;
        static_assert(std::is_same_v<std::remove_extent_t<ArrType>, std::remove_all_extents_t<ArrType>> && "Only 1 dimensional array allowed.");
        constexpr static std::array<std::remove_cvref_t<std::remove_all_extents_t<ArrType>>, std::extent_v<ArrType>> value = {Arr[Indices]...};
    };
}
template<const auto &Arr>
struct TCArrayToCPPArray {
    constexpr static auto value = details::TCArrayToCPPArray<Arr>::value;
};



CB_NAMESPACE_END

#endif //CELESTIALBEING_BASE_META_HPP
